#include "Platform/OpenGL/openGLRendererAPI.h"

// #include <glad/glad.h>
#include "Core/log.h"
#include "openGLRendererAPI.h"

namespace CC {

	unsigned int DisplayModes[] = {
		GL_POINTS,
		GL_LINES,
		GL_LINE_LOOP,
		GL_LINE_STRIP,
		GL_TRIANGLES,
		GL_TRIANGLE_STRIP,
		GL_TRIANGLE_FAN
	};
	
	void OpenGLMessageCallback(
		unsigned source,
		unsigned type,
		unsigned id,
		unsigned severity,
		int length,
		const char* message,
		const void* userParam)
	{
		switch (severity)
		{
			case GL_DEBUG_SEVERITY_HIGH:         CC_CORE_CRITICAL(message); return;
			case GL_DEBUG_SEVERITY_MEDIUM:       CC_CORE_ERROR(message); return;
			case GL_DEBUG_SEVERITY_LOW:          CC_CORE_WARN(message); return;
			case GL_DEBUG_SEVERITY_NOTIFICATION: CC_CORE_TRACE(message); return;
		}		
	}

	void OpenGLRendererAPI::Init()
	{

	#ifdef CC_DEBUG
		// glEnable(GL_DEBUG_OUTPUT);
		// glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
		// glDebugMessageCallback((GLDEBUGPROC)OpenGLMessageCallback, nullptr);
		
		// glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, NULL, GL_FALSE);
	#endif

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		glEnable(GL_DEPTH_TEST);
		glEnable(GL_LINE_SMOOTH);		
		glEnable(GL_MULTISAMPLE);		
	}

	void OpenGLRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
	{
		glViewport(x, y, width, height);
	}

	void OpenGLRendererAPI::SetClearColor(const glm::vec4& color)
	{
		glClearColor(color.r, color.g, color.b, color.a);
	}

	void OpenGLRendererAPI::Clear()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	}

	void OpenGLRendererAPI::Clear(CapEnum bit)
	{
		glClear((GLbitfield)bit);
	}

	void OpenGLRendererAPI::ClearDepth()
	{
		glClearDepth(1.0f);
	}

	void OpenGLRendererAPI::DrawIndexed(const VertexArray* vertexArray, uint32_t indexCount, DisplayModesEnum mode)
	{
		vertexArray->Bind();
		uint32_t count = indexCount ? indexCount : const_cast<VertexArray*>(vertexArray)->GetIndexBuffer()->GetCount();
		glDrawElements(DisplayModes[mode], count, GL_UNSIGNED_INT, nullptr);
	}

    void OpenGLRendererAPI::DrawArrays(const VertexArray * vertexArray, uint32_t count, DisplayModesEnum mode)
    {
        vertexArray->Bind();
        glDrawArrays(DisplayModes[mode], 0, count);
    }

	void OpenGLRendererAPI::DrawLines(const VertexArray* vertexArray, uint32_t vertexCount)
	{
		vertexArray->Bind();
		glDrawArrays(GL_LINES, 0, vertexCount);
	}

	void OpenGLRendererAPI::SetLineWidth(float width)
	{
		glLineWidth(width);
	}

    void OpenGLRendererAPI::UnBindTexture()
    {
		glBindTexture(GL_TEXTURE_2D, 0);
    }

    void OpenGLRendererAPI::BindTexture(uint32_t slot, uint32_t ID)
    {
		glActiveTexture(GL_TEXTURE0 + slot);
		glBindTexture(GL_TEXTURE_2D, ID);
    }
    void OpenGLRendererAPI::CullFace(CullFaceEnum type)
    {
		switch((int)type){
			case (int)CullFaceEnum_FRONT: glCullFace(GL_FRONT); break;
			case (int)CullFaceEnum_BACK: glCullFace(GL_BACK); break;
			case (int)CullFaceEnum_FRONT_AND_BACK: glCullFace(GL_FRONT_AND_BACK); break;
		}		
    }

	void OpenGLRendererAPI::SetEnableState(CapEnum cap, bool enable)
    {
		if(enable){
			glEnable(cap);
		}	
		else{
			glDisable(cap);
		}
    }

    void OpenGLRendererAPI::SetDepthFunc(DepthFuncEnum func)
    {
        glDepthFunc(func);
    }
}
